home *** CD-ROM | disk | FTP | other *** search
- /***
- *diskio.c - disk editor drive initialization/read-write module
- *
- *Copyright (c) 1991-1994, Gregg Jennings. All wrongs reserved.
- * P O Box 200, Falmouth, MA 02541-0200
- *
- *Purpose:
- * MS(tm)-DOS Disk EDitor. Handles low-level DOS Sector reads/writes.
- *
- *Notice:
- * This progam may be freely used and distributed. Any distrubution
- * with modifications must retain the above copyright statement and
- * modifications noted.
- * No pulp-publication, in whole or in part, permitted without
- * permission (magazines or books).
- *******************************************************************************/
-
- /*
-
- Version: 2.1 28-Nov-1993
-
- Release Notes:
-
- Uses Mircosoft/Borland specific CPU Register structure definitions.
- MUST BE COMPILED IN LARGE MODEL (far data), the references to
- SREGS will have to be modified for near data models.
-
- Uses DOS Interrupts 25h and 26h. Microsoft intdos() familiy of
- functions handles the popping of the flags after these Interrupts.
- I have not yet verified this aspect with Borlands intdos() functions.
-
- Borland compilers supply functions similar to mine: absread() and
- abswrite(), which correspond to sector_io().
-
- sector_io() can probably be easily changed to handle Interrupt 13
- serivices.
-
- */
-
- #include <stdio.h>
- #include <dos.h>
- #include <stdlib.h>
- #include <malloc.h>
- #include <memory.h>
-
- #include "disked.h"
- #include "diskio.h"
- #include "dpb.h"
- #include "alloc.h"
- #include "error.h"
-
- /* DOS interrupts */
-
- #define DOS_READ 0x25
- #define DOS_WRITE 0x26
-
- /* DOS error codes */
-
- #define FUNCTION_INVALID 1
- #define _DRIVE_NREADY 21
-
- static int returnerror(int minor);
- static int get_block_info(int drive, struct DPB *dpb);
- static int sector_io(int rw, int disk, unsigned long sector, unsigned char *buffer);
- static int xsector_io(int rw, int disk, unsigned long sector, unsigned char *buffer);
-
- static char ebuf[40];
-
- /* globals for constant drive values */
-
- /* physical values */
-
- unsigned long drive_size; /* drive size in bytes */
- unsigned int sec_size; /* sector size, usually 512 bytes */
- unsigned int max_head; /* maximum disk head number */
- unsigned int max_sector; /* maximum sector number */
- unsigned int max_track; /* maximum track number */
- unsigned long hidden_secs; /* number of hidden sectors on a hard drive */
- /* Partition info, first track */
- unsigned int reserved_secs; /* number of reserved sectors */
- /* Boot sector(s) */
-
- /* logical (DOS) values */
-
- unsigned long num_sectors; /* total number of sectors */
- unsigned int secs_cluster; /* sectors per cluster */
- unsigned int num_clusters; /* maximum cluster no. */
- unsigned int cluster_size; /* cluster size in bytes */
- unsigned int dir_sectors; /* number of sectors of root */
- unsigned int secs_fat; /* sectors per FAT */
- unsigned int num_fats; /* number of FATs */
- unsigned int dir_entries; /* number of directory entries */
- unsigned int data_sector; /* first data sector */
- unsigned int dir_sector; /* start of root directory */
- int fat_size; /* 12 or 16 */
- char format[10]; /* name of format */
- char volume[13]; /* volume label */
- char fatsize[7]; /* FAT size, DOS 4+ */
- unsigned int avail_clusters;
-
- /* globals for variable drive values */
-
- unsigned int max_drive; /* maximum drive number */
- unsigned long log_sector; /* logical sector no. */
- unsigned int disk; /* drive number 0+ */
- unsigned int head; /* physical head no. 0-max_head */
- unsigned int track; /* physical track no. 0-max_track */
- unsigned int sector; /* physical sector number 1-max_sector */
- unsigned char *sec_buf; /* sector buffer */
-
- int diskio_error;
-
- /*
- * ERROR MESSAGES
- * Based on the PC/MS-DOS number returned in the AL
- * register by the DOS interrupts 0x25 and 0x26.
- *
- */
- static const char *module_name = "diskio";
- static const char *diskerr_msg[]={
- "Write Protected Disk",
- "Unknown Unit",
- "Drive Not Ready",
- "Unknown Command",
- "Data Error (CRC)",
- "Bad Request Structure Length",
- "Seek Error",
- "Unknown Media Type",
- "Sector Not Found",
- "Printer Out of Paper",
- "Write Fault",
- "Read Fault",
- "General Failure",
- "Reserved Error",
- "Reserved Error",
- "Invalid Disk Change", /* end DOS */
-
- "BOOT sector invalid",
- "Can not Log network drives",
- "Sector out of bounds (oh oh...)",
- "Not Enough Memory",
-
- };
- enum DISKIO_MSGS {
- DISK_BOOT_ERR = 16, DISK_REMOTE, DISK_BOUNDS, DISK_MEM_ERR,
- NUM_ERRORS,
- };
-
-
- /*
- Yuck this is confusing. If exterror() was called to set the
- error code (minor), the code may not be right (not match the
- error message in diskerr_msg) on some machines. The same
- program on the same bootable floppy may give different responses
- on different computers. It is a combination of OS, Drives and
- BIOS.
- */
-
- static int returnerror(int minor)
- {
- diskio_error = 1;
- error.num = minor;
- error.mod = module_name;
- error.func = module_name;
-
- if (minor >= 0 && minor < NUM_ERRORS)
- error.msg = diskerr_msg[minor];
- /****
- else if (minor == DISK_CORRUPT)
- error.msg = err_msg[BOOT_ERROR];
- else if (minor == DISK_MEM_ERR)
- error.msg = err_msg[NO_MEM];
- else if (minor == DISK_REMOTE)
- error.msg = err_msg[DISK_REMOTE];
- else if (minor == DISK_BOUNDS)
- error.msg = err_msg[DISK_BOUNDS];
- ****/
- else
- {
- error.msg = "DOS error code: ";
- sprintf(ebuf,"%02Xh",minor);
- error.arg = ebuf;
- }
- if (minor == 0) /* KLUDGE! */
- minor = 10;
- return minor;
- }
-
- /*
- Disk Input/Output function.
-
- Pass command, argument, and pointer to sector size buffer.
-
- Returns DISK_OK (0) if ok
- less than 0 if a known error
- greater than zero for a DOS error.
-
- See DISKIO.H for error values.
-
- ver 2.0 13-Nov-1993 fixed a not free mis-match bug (INT 21, 36)
- consoladated error returns
- */
-
- /*
- MSC 7.00 really fucked up on me when some optimizations where used
- by corrupting the DPB pointer (MSC alias type problem). This function
- is "the heart" of DISKED. It MUST be bulletproof! The DPB actually
- need not be a pointer but this function is 2-depth recursive so
- stack calculations must be doubled.
- */
-
- #ifdef _MSC_VER
- #pragma optimize("e",off) /* don't fuck with stack frame */
- #endif
-
- int diskio(int command, long arg, unsigned char *buffer)
- {
- int i;
- int tempd;
- unsigned tempu;
- unsigned long templ;
- static unsigned long saved_log_sector;
- unsigned long tsector;
- static int extended;
- int boot;
- struct DPB *dpb;
- struct BOOT buf;
- unsigned char *tbuf;
- union REGS regs;
- int error, status;
-
- error = status = 0;
-
- switch(command) /* cases that return (if OK) are indicated */
- {
- case DISK_SAVE: /* save position, returns */
- saved_log_sector = log_sector;
- return(DISK_OK);
- break;
-
- case DISK_REST: /* restore saved position, returns */
- log_sector = saved_log_sector;
- return(DISK_OK);
- break;
-
- case DISK_INC: /* move, falls through */
- /*
- Increment (or decrement) position, wrapping
- around at the end or beginning.
- */
- tsector = log_sector;
- if ( (log_sector+=arg) >= num_sectors)
- {
- if (arg<0L)
- log_sector = num_sectors + arg + tsector;
- else
- log_sector -= num_sectors;
- }
- break;
-
- case DISK_SET: /* set position, falls through */
- /*
- Set the logical sector to the argument unless
- it is not valid.
- */
- if (arg<0L || (unsigned long)arg>=num_sectors)
- {
- error = 1;
- status = DISK_BOUNDS;
- }
- else
- log_sector = arg;
- break;
-
- case DISK_READ: /* read sector, returns unless error */
- /*
- * If arg == -1 use current log_sector, else use arg.
- */
- tsector = (arg>=0L) ? arg : log_sector;
- if (extended)
- status = xsector_io(DOS_READ,disk-1,tsector,buffer);
- else
- status = sector_io(DOS_READ,disk-1,tsector,buffer);
- if (status == -1)
- {
- diskio_error = 0;
- if (save_sec)
- memcpy(save_sec,sec_buf,sec_size);
- return(DISK_OK);
- }
- error = 1;
- break;
-
- case DISK_WRITE: /* write sector, returns unless error */
- /*
- * Same as DISK_READ (not too much code to duplicate it)
- */
- tsector = (arg>=0L) ? arg : log_sector;
- if (extended)
- status = xsector_io(DOS_WRITE,disk-1,tsector,buffer);
- else
- status = sector_io(DOS_WRITE,disk-1,tsector,buffer);
- if (status == -1)
- {
- diskio_error = 0;
- return(DISK_OK);
- }
- error = 1;
- break;
-
- case DISK_INIT: /* initialize the disk, returns unless error */
- /*
- Argument is 1 for A:, 2 for B: etc.
- Returns:
- DISK_OK,
- DISK_MEM_ERR not enough memory,
- DISK_BOOT_ERR bad boot sector,
- DISK_NREADY drive not ready.
- */
-
- boot = 0;
- avail_clusters = 0;
-
- if ((dpb = (struct DPB *)alloc(1,sizeof(struct DPB))) == NULL)
- {
- error = 1;
- status = DISK_MEM_ERR;
- break;
- }
- error = -1; /* if error, flag that dpb must be freed */
-
- /* get sector and drive size */
-
- if ((tempd = get_block_info((int)arg,dpb)) == 0)
- {
- tempu = dpb->bpb.sec_size;
- templ = (dword)dpb->bpb.num_sectors;
- if (templ == 0L)
- templ = dpb->bpb.total_sectors;
- templ *= (dword)tempu;
- }
- else if (tempd != FUNCTION_INVALID)
- {
- if (tempd == _DRIVE_NREADY) /* catch known errors */
- status = DISK_NREADY;
- else
- status = tempd;
- break;
- }
-
- /* use BOOT sector if get_block_info() failed or floppy drive */
-
- if (tempd != 0 || dpb->bpb.media_desc != 0xF8)
- {
- boot = 1; /* flag must use boot data */
- regs.x.ax = 0x3600;
- regs.x.dx = (int)arg;
- intdos(®s,®s);
- if (regs.x.ax == 0xffff)
- {
- status = exterror();
- break;
- }
- tempu = regs.x.cx;
- templ = (dword)tempu * (dword)regs.x.dx * (dword)regs.x.ax;
- avail_clusters = regs.x.bx;
- }
-
- /* test for extended i/o type (>32M) */
-
- if (templ >= 33554432L)
- extended = 1;
- else
- extended = 0;
-
- /* get buffer for boot sector */
-
- if ((tbuf=(unsigned char *)alloc(tempu,sizeof(char)))==NULL)
- {
- status = DISK_MEM_ERR;
- break;
- }
- error = -2; /* if error, flag must free tbuf as well */
-
- if (extended)
- status = xsector_io(DOS_READ,(int)arg-1,0,tbuf);
- else
- status = sector_io(DOS_READ,(int)arg-1,0,tbuf);
-
- if (status != -1)
- break;
-
- /* quick and simple check for bad boot record */
-
- if ((tbuf[0]!=(byte)0xeb && tbuf[0]!=(byte)0xe9 && tbuf[0]!=0) \
- || (tbuf[2]!=(byte)0x90 && tbuf[2]!=0))
- {
- if (boot == 1)
- {
- status = DISK_BOOT_ERR;
- break;
- }
- }
- /* disk is valid */
-
- disk = (int)arg;
- drive_size = templ;
- sec_size = tempu;
-
- volume[0]='\0';
- for (i=0;i<8;i++)
- format[i]=tbuf[i+3];
- format[i]='\0';
-
- if (boot == 1)
- memcpy(&buf,tbuf,sizeof(struct BOOT));
- else
- memcpy(&buf.sec_size,&dpb->bpb,sizeof(struct BPB));
-
- freep(tbuf); /* all done with these */
- freep(dpb);
-
- secs_cluster = buf.secs_cluster;
- reserved_secs = buf.reserved_secs;
- num_fats = buf.num_fats;
- dir_entries = buf.dir_entries;
- num_sectors = (dword)buf.num_sectors;
- secs_fat = buf.secs_fat;
- max_sector = buf.secs_track;
- max_head = buf.num_heads;
- hidden_secs = buf.hidden_sectors;
- if (num_sectors==0L)
- num_sectors = buf.total_sectors;
-
- dir_sectors = dir_entries/(sec_size/32);
- data_sector = (secs_fat*num_fats) + dir_sectors + reserved_secs-1;
- dir_sector = (secs_fat*num_fats) + reserved_secs;
- cluster_size = sec_size * secs_cluster;
- drive_size = (long)sec_size * (long)num_sectors;
- num_clusters =
- (word)(((num_sectors-((dword)secs_fat*(dword)num_fats)-(dword)dir_sectors-1)
- / (dword)secs_cluster)+1);
- max_track =
- (word)((((num_sectors+hidden_secs)/(dword)max_head)
- / (dword)max_sector)-1);
-
- fat_size = (num_clusters < 4096) ? 12 : 16;
- _dos_setdrive(disk,&i);
-
- diskio_error = 0;
- return(DISK_OK);
- break;
-
- default:
- error = 1;
- status = 3;
- break;
- }
-
- if (error == 0)
- {
- /* Set physical parameters and then read sector */
-
- templ = log_sector+hidden_secs;
-
- track = (word)(templ/((dword)max_sector*(dword)max_head));
- sector = (word)(templ%((dword)max_sector)+1);
- head = (word)(templ%((dword)max_sector*(dword)max_head));
- head /= max_sector;
-
- return(diskio(DISK_READ,-1L,buffer));
- }
-
- if (error == -2)
- {
- freep(dpb);
- freep(tbuf);
- }
- if (error == -1)
- freep(dpb);
-
- return returnerror(status);
- }
- #ifdef _MSC_VER
- #pragma optimize("",on)
- #endif
-
- /* ncluster() next or previous cluster
-
- Increment or decrement position by clusters.
- If gone past the last cluster go to the first.
- If gone before the first go to the last.
-
- Returns the logical sector number of the first sector
- of the cluster.
- */
-
- unsigned long ncluster(register int arg)
- {
- dword temp = log_sector;
-
- if (arg>0)
- temp+=(dword)secs_cluster;
- else
- temp-=(dword)secs_cluster;
- temp -= clustersector(log_sector);
- if (temp<data_sector || temp>=num_sectors)
- {
- if (arg>0) /* first cluster */
- return(data_sector+1);
- else /* last cluster */
- return(clustertosector(num_clusters));
- }
- return(temp);
- }
-
- /* Physical/Logical conversion routines.
- * I figured all of this stuff out by myself with the help of a
- * programmable calculator. I have never found any of this stuff
- * documented anywhere -- it may be somewhere, I just never found it.
- */
-
- /* convert logical sector number to the sector within the cluster number */
-
- word clustersector(dword sector)
- {
- if (sector <= (dword)data_sector)
- return 0;
- return (word) ((sector - (dword)(data_sector+1)) % (dword)secs_cluster);
- }
-
- /* convert physical parameters to logical sector number */
-
- dword logicalsector(word track, word sector, word head)
- {
- dword trk;
-
- head *= max_sector;
- trk = track * max_head * max_sector;
- return (trk + (dword)head + (dword)(sector-1)) - hidden_secs;
- }
-
- /* convert cluster number to logical sector number */
-
- dword clustertosector(word cluster)
- {
- return (dword) (((dword)(cluster-2) * (dword)secs_cluster)
- + (dword)(data_sector+1));
- }
-
- /* convert the logical sector number to the cluster number */
-
- word sectortocluster(dword sector)
- {
- if (sector <= (dword)data_sector)
- return 0;
- return (word)
- ( ( (sector-(dword)(data_sector+1)) / (dword)secs_cluster ) + 2);
- }
-
- /* get_block_info()
-
- Gets drive parameters from DOS Int 21 function 44 sub-function D.
-
- Note: The segment regs are not needed (and FP_SEG wont work)
- if compiled with the SMALL memory model.
-
- Returns 0 for OK.
-
- */
-
- static int get_block_info(int drive, struct DPB *dpb)
- {
- union REGS regs;
- struct SREGS sregs;
-
- regs.x.ax=0x4409;
- regs.x.bx=(unsigned)drive;
- intdos(®s,®s);
- if (regs.x.cflag)
- return(exterror());
- if (regs.x.dx & (1<<12))
- return(DISK_REMOTE);
- segread(&sregs);
- regs.x.ax=0x440d;
- regs.x.bx=(unsigned)drive;
- regs.x.cx=0x0860;
- regs.x.dx=FP_OFF(dpb);
- sregs.ds =FP_SEG(dpb);
- intdosx(®s,®s,&sregs);
- if (regs.x.cflag)
- {
- if (regs.x.ax == 1) /* 1 = function not supported */
- return 1; /* e.g. ramdrive */
- else
- return(exterror());
- }
- return(0);
- }
-
- /*
- Actual sector IO functions.
-
- Returns -1 for OK.
- */
- static sector_io(int rw, int disk, unsigned long sector, unsigned char *buffer)
- {
- union REGS regs;
- struct SREGS sregs;
-
- regs.x.ax=disk;
- regs.x.dx=(unsigned int)sector;
- regs.x.cx=1U;
- regs.x.bx=FP_OFF(buffer);
- segread(&sregs);
- sregs.ds=FP_SEG(buffer);
- int86x(rw,®s,®s,&sregs);
- if (regs.x.cflag)
- return(regs.h.al);
- return -1;
- }
-
- /* Extended sector I/O */
-
- static xsector_io(int rw, int disk, unsigned long sector, unsigned char *buffer)
- {
- union REGS regs;
- struct SREGS sregs;
- struct DCB Dcb;
- struct DCB *dcb=&Dcb;
-
- regs.x.ax=disk;
- dcb->sector=sector;
- dcb->number=1U;
- dcb->buffer=buffer;
- regs.x.cx=0xffff;
- regs.x.bx=FP_OFF(dcb);
- segread(&sregs);
- sregs.ds=FP_SEG(dcb);
- int86x(rw,®s,®s,&sregs);
- if (regs.x.cflag)
- return(regs.h.al);
- return -1;
- }
-
- /* Get DOS Extended Error information */
-
- #ifdef _MSC_VER
-
- # if _MSC_VER >= 700 /* MSC v7.00 */
-
- #pragma optimize("gle",off)
- #pragma warning(disable:4035)
-
- int exterror()
- {
- _asm mov ax,5900h
- _asm xor bx,bx
- _asm int 21h
- }
- # else /* MSC v6.00 and QuickC v2.5 */
-
- int exterror()
- {
- int errcode; /* the use of errcode is not necessary */
- /* cuz the value in AX is what is normaly */
- _asm mov ax,5900h /* returned, it just eliminates a compiler */
- _asm xor bx,bx /* warning "no return value" */
- _asm int 21h
- _asm mov errcode,ax
-
- return errcode;
- }
-
- # endif
-
- #else /* non-MSC or MSC < 6.00 */
-
- int exterror(void)
- {
- if (_osmajor >= 3)
- return dosexterr(NULL);
- return 1;
- }
-
- #endif
-